﻿using LightCAD.Core.Elements;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using LightCAD.MathLib;

namespace LightCAD.Core
{
    /// <summary>
    /// 元素基类
    /// </summary>
    public abstract partial class LcElement : LcObject
    {

        private object _action;
        protected Box2 _boudingBox;

        private uint? colorValue;
        private string color = ValueFrom.ByLayer;
        private string name;
        private string layer = "0";
        private LcLayer layerValue;

        [JsonIgnore]
        public ElementType Type { get; protected set; }

        /// <summary>
        /// 宿主元素，用于在位编辑
        /// </summary>
        [JsonIgnore]
        public LcElement Host { get; set; }

        [DefaultValue("0")]
        public string Layer
        {
            get
            {
                return this.layer;
            }
            set
            {
                this.layer = value;
                this.layerValue = this.Document.Layers.GetByName(value);
            }
        }

        [DefaultValue(ValueFrom.ByLayer)]
        public string Color
        {
            get
            {
                return this.color;
            }
            set
            {
                this.color = value;
                this.colorValue = null;
            }
        }

        [DefaultValue(ValueFrom.ByLayer)]
        public string LineType { get; set; } = ValueFrom.ByLayer;

        [DefaultValue(ValueFrom.ByLayer)]
        public string LineWeight { get; set; } = ValueFrom.ByLayer;

        [DefaultValue(1.0)]
        public double LineTypeScale { get; set; } = 1.0;

        [DefaultValue(ValueFrom.ByLayer)]
        public string Transparency { get; set; } = ValueFrom.ByLayer;

        [DefaultValue(ValueFrom.ByColor)]
        public string PlotStyleName { get; set; } = ValueFrom.ByColor;

        [DefaultValue(true)]
        public bool Visible { get; set; } = true;
        public Dictionary<string, object> UserData { get; set; }

        public LcLayer GetLayerValue()
        {
            if (layerValue == null)
            {
                this.layer = this.Document.Layers.Where(x => x.IsStatus == true).FirstOrDefault().Name;
                layerValue = this.Document.Layers.GetByName(this.layer);
            }
            return layerValue;
        }
        public uint GetColorValue()
        {
            //if (colorValue == null)
            //{
                if (this.color == ValueFrom.ByLayer)
                {
                    colorValue = GetLayerValue().Color;
                }
                else if (this.color == ValueFrom.ByBlock)
                {
                    colorValue = 0xFF000000;
                }
                else
                {
                    string PATTERN = @"([^A-Fa-f0-9]|\s+?)+";
                    if (uint.TryParse(this.color, out uint c))
                    {
                        colorValue = c;
                    }
                    else if (Regex.IsMatch(this.color, PATTERN))
                    {
                        var html = ColorTranslator.FromHtml(this.color);
                        colorValue = (uint)html.ToArgb();
                    }
                    else
                    {
                        colorValue = 0xFF000000;
                    }
                }
            //}
            return colorValue.Value;
        }



        [JsonIgnore]
        public virtual bool IsSelected { get; set; }

        [JsonIgnore]
        public virtual bool IsDirty { get; set; }


        /// <summary>
        /// 运行时状态
        /// </summary>
        [JsonIgnore]
        public ElementStatus RtStatus { get; set; }

        public void SetStatus(ElementStatus status, bool state)
        {
            if (state)
                RtStatus |= status;
            else
                RtStatus &= ~status;
        }
        public bool HasStatus(ElementStatus status)
        {
            return (this.RtStatus & status) == status;
        }


        /// <summary>
        /// 运行时行为对象
        /// </summary>
        [JsonIgnore]
        public object RtAction
        {
            get
            {
                if (_action == null)
                {
                    LcDocument.ElementActions.TryGetValue(this.Type, out _action);
                }
                if (_action == null)
                {
                    throw new Exception($"Can't find Action. (ElementType={this.Type})");
                }
                return _action;
            }
        }




        public void SetVisible(bool visible, bool fireChangedEvent = true)
        {
            if (this.Visible == visible) return;
            //if (fireChangedEvent) OnPropertyChangedBefore(nameof(Visible), this.Visible, visible);
            var oldValue = this.Visible;
            this.Visible = visible;
            if (fireChangedEvent) OnPropertyChangedAfter(nameof(Visible), oldValue, this.Visible);
        }
        [JsonIgnore]
        public Box2 BoundingBox
        {
            get
            {
                if (this._boudingBox == null)
                {
                    this._boudingBox = GetBoundingBox();
                }
                return this._boudingBox;
            }
        }


        public virtual LcElement ApplyMatrix(Matrix3 matrix)
        {
            return this;
        }
        public virtual Box2 GetBoundingBox()
        {
            return new Box2();
        }
        public virtual Box2 GetBoundingBox(Matrix3 matrix)
        {
            return new Box2();
        }
        public void ResetBoundingBox()
        {
            this._boudingBox = null;
        }
        public virtual void Translate(double dx, double dy)
        {
        }
        public virtual void Translate(Vector2 vec)
        {
            this.Translate(vec.X, vec.Y);
        }
        public virtual void CopyStyle(LcElement src)
        {
            this.Layer = src.Layer;
            this.Color = src.Color;
            this.LineType = src.LineType;
            this.LineTypeScale = src.LineTypeScale;
            this.LineWeight = src.LineWeight;
            this.Transparency = src.Transparency;
            this.PlotStyleName = src.PlotStyleName;
        }
        public virtual void Copy(LcElement src)
        {
            base.Copy(src);
            this.Visible = src.Visible;
            CopyStyle(src);
        }


        public virtual void Move(Vector2 startPoint, Vector2 endPoint)
        {
        }
        public virtual void Scale(Vector2 basePoint, double scaleFactor)
        {

        }
        public virtual void Scale(Vector2 basePoint, Vector2 scaleVector)
        {

        }
        public virtual void Rotate(Vector2 basePoint, double rotateAngle)
        {

        }
        public virtual void Mirror(Vector2 axisStart, Vector2 axisEnd)
        {

        }

        protected override void OnPropertyChanged(string propertyName, LcPropertyGroup extPropGroup, LcProperty extProp)
        {
            ResetBoundingBox();
        }
        /// <summary>
        /// 是否和盒子相交
        /// </summary>
        /// <param name="testBox">测试盒子</param>
        /// <param name="eleMatrix">元素的转换矩阵</param>
        /// <returns></returns>
        public virtual bool IntersectWithBox(Polygon2d testPoly, List<RefChildElement> intersectChildren = null)
        {
            return false;
        }
        /// <summary>
        /// 是否被盒子包含
        /// </summary>
        /// <param name="testBox">测试盒子</param>
        /// <param name="eleMatrix">元素的转换矩阵</param>
        /// <returns></returns>
        public virtual bool IncludedByBox(Polygon2d testPoly, List<RefChildElement> includedChildren = null)
        {
            return false;
        }

        public abstract LcElement Clone();

        /// <summary>
        /// 此方法获取元素的几何曲线，用于各类计算
        /// </summary>
        /// <returns></returns>
        public virtual Curve2d[] GetCurves()
        {
            return new Curve2d[0];
        }
    }

    public class RefChildElement
    {
        public LcElement Element { get; set; }
        public IElementContainerRef RefObject { get; set; }

        public RefChildElement(LcElement element, IElementContainerRef refObject)
        {
            Element = element;
            RefObject = refObject;
        }
    }


}